package com.kexio.file.storage.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.kexio.file.config.FileStorageProperties;
import com.kexio.file.storage.StorageFileInfo;
import com.kexio.file.storage.StorageProvider;
import com.kexio.file.storage.StorageResult;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.google.gson.Gson;

/**
 * 七牛云Kodo存储提供者
 * 
 * 基于七牛云对象存储服务的文件存储实现
 * 
 * @author Kexio Team
 * @since 1.0.0
 */
@Component
@ConditionalOnClass(name = "com.qiniu.util.Auth")
public class QiniuStorageProvider implements StorageProvider {
    
    private static final Logger log = LoggerFactory.getLogger(QiniuStorageProvider.class);
    
    @Autowired
    private FileStorageProperties storageProperties;
    
    private volatile Auth auth;
    private volatile UploadManager uploadManager;
    private volatile BucketManager bucketManager;
    private volatile Configuration configuration;
    
    /**
     * 获取Auth对象（懒加载）
     */
    private Auth getAuth() {
        if (auth == null) {
            synchronized (this) {
                if (auth == null) {
                    initializeQiniuClient();
                }
            }
        }
        return auth;
    }
    
    /**
     * 获取UploadManager（懒加载）
     */
    private UploadManager getUploadManager() {
        if (uploadManager == null) {
            getAuth(); // 触发初始化
        }
        return uploadManager;
    }
    
    /**
     * 获取BucketManager（懒加载）
     */
    private BucketManager getBucketManager() {
        if (bucketManager == null) {
            getAuth(); // 触发初始化
        }
        return bucketManager;
    }
    
    /**
     * 初始化七牛云客户端
     */
    private void initializeQiniuClient() {
        try {
            FileStorageProperties.QiniuStorageConfig qiniuConfig = storageProperties.getQiniu();
            
            // 创建认证对象
            auth = Auth.create(qiniuConfig.getAccessKey(), qiniuConfig.getSecretKey());
            
            // 创建配置对象，指定存储区域
            configuration = new Configuration(getRegionByCode(qiniuConfig.getRegion()));
            configuration.useHttpsDomains = qiniuConfig.isUseHttps();
            
            // 创建上传管理器
            uploadManager = new UploadManager(configuration);
            
            // 创建存储桶管理器
            bucketManager = new BucketManager(auth, configuration);
            
            log.info("七牛云Kodo客户端初始化成功: region={}, useHttps={}", 
                    qiniuConfig.getRegion(), qiniuConfig.isUseHttps());
            
        } catch (Exception e) {
            log.error("七牛云Kodo客户端初始化失败", e);
            throw new RuntimeException("七牛云Kodo客户端初始化失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String getStorageType() {
        return "qiniu";
    }
    
    @Override
    public StorageResult storeFile(String bucketName, String objectKey, InputStream inputStream,
                                  long contentLength, String contentType, Map<String, String> metadata) {
        try {
            // 读取输入流数据
            byte[] data = readInputStream(inputStream);
            
            // 生成上传凭证
            StringMap putPolicy = new StringMap();
            if (StringUtils.hasText(contentType)) {
                putPolicy.put("mimeLimit", contentType);
            }
            String uploadToken = getAuth().uploadToken(bucketName, objectKey, 3600, putPolicy);
            
            // 执行上传
            Response response = getUploadManager().put(data, objectKey, uploadToken);
            
            if (response.isOK()) {
                DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
                
                // 构建存储结果
                StorageResult storageResult = new StorageResult();
                storageResult.setBucketName(bucketName);
                storageResult.setObjectKey(objectKey);
                storageResult.setFileSize((long) data.length);
                storageResult.setContentType(contentType);
                storageResult.setEtag(putRet.hash);
                storageResult.setStorageUrl(buildStorageUrl(bucketName, objectKey));
                storageResult.setMetadata(metadata);
                storageResult.setSuccess(true);
                
                log.debug("七牛云Kodo文件上传成功: bucket={}, key={}, size={}, hash={}", 
                         bucketName, objectKey, data.length, putRet.hash);
                
                return storageResult;
            } else {
                throw new RuntimeException("上传失败: " + response.bodyString());
            }
            
        } catch (Exception e) {
            log.error("七牛云Kodo文件上传失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("七牛云Kodo文件上传失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public InputStream getFile(String bucketName, String objectKey) {
        try {
            // 生成下载URL
            String downloadUrl = buildDownloadUrl(bucketName, objectKey);
            
            // 七牛云下载需要通过HTTP请求实现
            // 这里简化处理，实际使用时建议通过HTTP客户端实现
            throw new UnsupportedOperationException("七牛云Kodo暂不支持直接获取InputStream，请使用generatePreSignedDownloadUrl获取下载链接");
            
        } catch (Exception e) {
            log.error("七牛云Kodo文件获取失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("七牛云Kodo文件获取失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public boolean deleteFile(String bucketName, String objectKey) {
        try {
            BucketManager bucketManager = getBucketManager();
            
            Response response = bucketManager.delete(bucketName, objectKey);
            
            if (response.isOK()) {
                log.debug("七牛云Kodo文件删除成功: bucket={}, key={}", bucketName, objectKey);
                return true;
            } else {
                log.warn("七牛云Kodo文件删除失败: bucket={}, key={}, response={}", 
                        bucketName, objectKey, response.bodyString());
                return false;
            }
            
        } catch (QiniuException e) {
            log.error("七牛云Kodo文件删除失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    @Override
    public boolean fileExists(String bucketName, String objectKey) {
        try {
            BucketManager bucketManager = getBucketManager();
            
            FileInfo fileInfo = bucketManager.stat(bucketName, objectKey);
            return fileInfo != null;
            
        } catch (QiniuException e) {
            if (e.response.statusCode == 612) {
                // 文件不存在
                return false;
            }
            log.error("七牛云Kodo文件存在性检查失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    @Override
    public StorageFileInfo getFileInfo(String bucketName, String objectKey) {
        try {
            BucketManager bucketManager = getBucketManager();
            
            FileInfo fileInfo = bucketManager.stat(bucketName, objectKey);
            
            StorageFileInfo result = new StorageFileInfo();
            result.setFileSize(fileInfo.fsize);
            result.setContentType(fileInfo.mimeType);
            result.setLastModified(toLocalDateTime(new Date(fileInfo.putTime / 10000))); // 七牛时间戳是100纳秒
            result.setEtag(fileInfo.hash);
            result.setExists(true);
            
            return result;
            
        } catch (QiniuException e) {
            log.error("七牛云Kodo文件信息获取失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("七牛云Kodo文件信息获取失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String generatePreSignedUploadUrl(String bucketName, String objectKey,
                                           int expireMinutes, String contentType) {
        try {
            // 生成上传凭证
            StringMap putPolicy = new StringMap();
            if (StringUtils.hasText(contentType)) {
                putPolicy.put("mimeLimit", contentType);
            }
            
            String uploadToken = getAuth().uploadToken(bucketName, objectKey, expireMinutes * 60, putPolicy);
            
            // 七牛云的上传URL是固定的，通过token控制权限
            String uploadUrl = "https://upload.qiniup.com";
            return uploadUrl + "?uploadToken=" + uploadToken;
            
        } catch (Exception e) {
            log.error("七牛云Kodo预签名上传URL生成失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("七牛云Kodo预签名上传URL生成失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public String generatePreSignedDownloadUrl(String bucketName, String objectKey, int expireMinutes) {
        try {
            String baseUrl = buildDownloadUrl(bucketName, objectKey);
            
            // 生成私有空间的下载链接
            long expireTime = System.currentTimeMillis() / 1000 + expireMinutes * 60;
            String downloadUrl = getAuth().privateDownloadUrl(baseUrl, expireTime);
            
            return downloadUrl;
            
        } catch (Exception e) {
            log.error("七牛云Kodo预签名下载URL生成失败: bucket={}, key={}", bucketName, objectKey, e);
            throw new RuntimeException("七牛云Kodo预签名下载URL生成失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public boolean copyFile(String sourceBucket, String sourceKey, String destBucket, String destKey) {
        try {
            BucketManager bucketManager = getBucketManager();
            
            Response response = bucketManager.copy(sourceBucket, sourceKey, destBucket, destKey);
            
            if (response.isOK()) {
                log.debug("七牛云Kodo文件复制成功: {}:{} -> {}:{}", sourceBucket, sourceKey, destBucket, destKey);
                return true;
            } else {
                log.warn("七牛云Kodo文件复制失败: {}:{} -> {}:{}, response={}", 
                        sourceBucket, sourceKey, destBucket, destKey, response.bodyString());
                return false;
            }
            
        } catch (QiniuException e) {
            log.error("七牛云Kodo文件复制失败: {}:{} -> {}:{}", sourceBucket, sourceKey, destBucket, destKey, e);
            return false;
        }
    }
    
    @Override
    public boolean createBucketIfNotExists(String bucketName) {
        try {
            BucketManager bucketManager = getBucketManager();
            
            // 七牛云需要通过控制台创建存储空间，SDK不支持创建
            // 这里只做存在性检查
            String[] buckets = bucketManager.buckets();
            for (String bucket : buckets) {
                if (bucket.equals(bucketName)) {
                    log.debug("七牛云Kodo存储空间已存在: {}", bucketName);
                    return true;
                }
            }
            
            log.warn("七牛云Kodo存储空间不存在，请在七牛云控制台创建: {}", bucketName);
            return false;
            
        } catch (QiniuException e) {
            log.error("七牛云Kodo存储空间检查失败: {}", bucketName, e);
            return false;
        }
    }
    
    @Override
    public boolean verifyFileIntegrity(String bucketName, String objectKey, String expectedHash) {
        try {
            FileInfo fileInfo = getBucketManager().stat(bucketName, objectKey);
            String actualHash = fileInfo.hash;
            
            boolean isValid = expectedHash != null && expectedHash.equals(actualHash);
            
            log.debug("七牛云Kodo文件完整性验证: bucket={}, key={}, expected={}, actual={}, valid={}", 
                     bucketName, objectKey, expectedHash, actualHash, isValid);
                     
            return isValid;
            
        } catch (QiniuException e) {
            log.error("七牛云Kodo文件完整性验证失败: bucket={}, key={}", bucketName, objectKey, e);
            return false;
        }
    }
    
    /**
     * 构建存储URL（公共空间）
     */
    private String buildStorageUrl(String bucketName, String objectKey) {
        FileStorageProperties.QiniuStorageConfig qiniuConfig = storageProperties.getQiniu();
        String domain = qiniuConfig.getDomain();
        
        if (StringUtils.hasText(domain)) {
            String protocol = qiniuConfig.isUseHttps() ? "https://" : "http://";
            return protocol + domain + "/" + objectKey;
        } else {
            // 如果没有配置域名，返回默认格式
            return "https://" + bucketName + ".qiniucdn.com/" + objectKey;
        }
    }
    
    /**
     * 构建下载URL
     */
    private String buildDownloadUrl(String bucketName, String objectKey) {
        return buildStorageUrl(bucketName, objectKey);
    }
    
    /**
     * 根据区域代码获取Region对象
     */
    private Region getRegionByCode(String regionCode) {
        switch (regionCode.toLowerCase()) {
            case "z0":
            case "huadong":
                return Region.huadong();
            case "z1":
            case "huabei":
                return Region.huabei();
            case "z2":
            case "huanan":
                return Region.huanan();
            case "na0":
            case "beimei":
                return Region.beimei();
            case "as0":
            case "xinjiapo":
                return Region.xinjiapo();
            default:
                log.warn("未知的七牛云区域代码: {}，使用默认华东区域", regionCode);
                return Region.huadong();
        }
    }
    
    /**
     * 读取InputStream数据
     */
    private byte[] readInputStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        byte[] data = new byte[1024];
        int bytesRead;
        while ((bytesRead = inputStream.read(data)) != -1) {
            buffer.write(data, 0, bytesRead);
        }
        return buffer.toByteArray();
    }
    
    /**
     * Date转LocalDateTime
     */
    private LocalDateTime toLocalDateTime(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }
}
